package com.healthdata.service.impl;

import com.healthdata.dao.HospitalKindMapper;
import com.healthdata.dao.MedicalInstitutionsRegionYearMapper;
import com.healthdata.service.CalculateService;
import com.healthdata.vo.CorrelationVO;
import com.healthdata.vo.TrendVO;
import org.apache.commons.math3.distribution.ExponentialDistribution;
import org.apache.commons.math3.distribution.NormalDistribution;
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
import org.apache.commons.math3.stat.descriptive.rank.Percentile;
import org.apache.commons.math3.stat.inference.KolmogorovSmirnovTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import weka.classifiers.functions.LinearRegression;
import weka.core.DenseInstance;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.Utils;




//CalculateService中主要包含的方法有计算均值、中位数、标准差、分布分析等基础统计功能
//相关性分析：支持变量间相关性计算与分析
//趋势分析：时间序列数据的趋势预测与分析
@Service
public class CalculateServiceImpl implements CalculateService {

    @Autowired
    private MedicalInstitutionsRegionYearMapper medicalInstitutionsRegionYearMapper;

    @Autowired
    private HospitalKindMapper hospitalKindMapper;

    //均值mean
    public double mean(double[] data) {
        //将数据存入DescriptiveStatistics变量，自动计算改组数据的一些统计值
        DescriptiveStatistics stats = new DescriptiveStatistics(data);
        return stats.getMean();
    }

    //中位数median
    public double median(double[] data) {
        //将数据存入DescriptiveStatistics变量，自动计算改组数据的一些统计值
        DescriptiveStatistics stats = new DescriptiveStatistics(data);
        stats.setPercentileImpl(new Percentile(50.0).withEstimationType(Percentile.EstimationType.R_7));
        return stats.getPercentile(50);
    }

    //标准差deviation
    public double deviation(double[] data) {
        //将数据存入DescriptiveStatistics变量，自动计算改组数据的一些统计值
        DescriptiveStatistics stats = new DescriptiveStatistics(data);
        return stats.getStandardDeviation();
    }

    //检验是否符合正太分布
    public boolean testNormal(double[] data) {
        //将数据存入DescriptiveStatistics变量，自动计算改组数据的一些统计值
        DescriptiveStatistics stats = new DescriptiveStatistics(data);

        // 拟合正态分布,构建理论上的正太分布模型
        NormalDistribution normalDist = new NormalDistribution(
                stats.getMean(),  //从stats中获取data数据组均值
                stats.getStandardDeviation() // 获取标准差
        );

        // 执行K-S检验，判断本组数据和理论上的正太分布差异
        KolmogorovSmirnovTest test = new KolmogorovSmirnovTest();
        double pValue = test.kolmogorovSmirnovTest(normalDist, data);

        if (pValue > 0.05) {
            return true;
        } else {
            return false;
        }
    }

    //检验是否符合指数分布
    public boolean testExponential(double[] data) {
        //计算样本统计量
        DescriptiveStatistics stats = new DescriptiveStatistics(data);
        double mean = stats.getMean();
        double stdDev = stats.getStandardDeviation();

        // 验证指数分布的特征（变异系数应接近1）
        //double cv = stdDev / mean;
        //System.out.println("变异系数: " + cv);

        //构建指数分布模型（λ = 1/均值）
        ExponentialDistribution expDist = new ExponentialDistribution(1.0 / mean);

        //执行K-S检验
        KolmogorovSmirnovTest test = new KolmogorovSmirnovTest();
        double pValue = test.kolmogorovSmirnovTest(expDist, data);

        if (pValue > 0.05) {
            return true;
        } else {
            return false;
        }
    }

    //判断线性相关,attributeIndex1和attributeIndex2表示在数据集中的索引，第几个属性
    public CorrelationVO calculateCorrelation(Instances data, int attributeIndex1, int attributeIndex2) {

        CorrelationVO correlationVO = new CorrelationVO();

        try {
            // 提取两个属性的所有值
            double[] values1 = data.attributeToDoubleArray(attributeIndex1);
            double[] values2 = data.attributeToDoubleArray(attributeIndex2);

            // 计算皮尔逊相关系数
            double correlation = Utils.correlation(values1, values2, data.numInstances());

            correlationVO.setCorrelation(correlation);
            correlationVO.setName1(data.attribute(attributeIndex1).name());
            correlationVO.setName2(data.attribute(attributeIndex2).name());

            if (correlation <= 1 && correlation >= -1) {
                correlationVO.setConform(true);
            } else {
                correlationVO.setConform(false);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return correlationVO;
    }

    //趋势分析
    public TrendVO predictTrend(Instances preparedData,int directTime,int attributeIndex1, int attributeIndex2) {
        //建立线性模型
        LinearRegression linearRegression = new LinearRegression();

        //判断是否为线性相关
        CorrelationVO correlationVO=calculateCorrelation(preparedData, attributeIndex1, attributeIndex2);
        TrendVO trendVO=new TrendVO();
        if(correlationVO.isConform())
        {
        try {
            // 设置是否输出模型方程等，这里简单配置
            linearRegression.setOptions(Utils.splitOptions("-S 0"));

            //设置要预测的列属性
            // 确保设置类属性（假设目标变量是数据集的最后一列）
            preparedData.setClassIndex(attributeIndex2);
            linearRegression.buildClassifier(preparedData);
            System.out.println("线性回归模型：" + linearRegression);

            // 趋势判断：获取时间索引列的系数（通常是第一个非截距系数）
            double[] coefficients = linearRegression.coefficients();
            double timeCoefficient = coefficients[0];//获取斜率
            trendVO.setSlope(timeCoefficient);
            trendVO.setIntercept(coefficients[3]);


            String trendDirection = timeCoefficient > 0 ? "上升" : "下降";

            trendVO.setTrend(trendDirection);
            trendVO.setStrength(Math.abs(timeCoefficient));//是斜率



            // 预测目标时间的值
            Instance predictionInstance = new DenseInstance(preparedData.numAttributes());
            predictionInstance.setDataset(preparedData);

            // 设置预测实例的特征值
            predictionInstance.setValue(attributeIndex1, directTime);

            double predictedValue = linearRegression.classifyInstance(predictionInstance);
            trendVO.setPrediction(predictedValue);
        } catch (Exception e) {
            e.printStackTrace();
        }
        }
        else
        {
            trendVO.setTrend("数据不是线性相关，不能预测");

        }
        return trendVO;
    }

}
